package edu.towson.cis.cosc442.project3.vendingmachine;

import static org.junit.Assert.*;

import org.junit.Test;
import org.junit.Before;
import org.junit.After;
import org.junit.rules.ExpectedException;
import org.junit.Rule;


public class VendingMachineTest {
	VendingMachine machine;
	VendingMachineItem item1;
	VendingMachineItem item2;
	VendingMachineItem item3;
	VendingMachineItem item4;
	
	@Rule public ExpectedException thrown = ExpectedException.none();
		
	//Sets up a machine with one item "Chips" that costs $0.75
	@Before
	public void setUp(){
		machine = new VendingMachine();
		item1 = new VendingMachineItem("Chips", .75);
	}
	
	//Tests if the an item is actually added to the correct location
	@Test
	public void testAddItemA(){
		machine.addItem(item1, "A");
		assertEquals(machine.getItem("A"), item1);
	}
	
	//Tests if the an item is actually added to the correct location
	@Test
	public void testAddItemB(){
		machine.addItem(item1, "B");
		assertEquals(machine.getItem("B"), item1);
	}
	
	//Tests if the an item is actually added to the correct location
	@Test
	public void testAddItemC(){
		machine.addItem(item1, "C");
		assertEquals(machine.getItem("C"), item1);
	}
	
	//Tests if the an item is actually added to the correct location
	@Test
	public void testAddItemD(){
		machine.addItem(item1, "D");
		assertEquals(machine.getItem("D"), item1);
	}
	
	//Tests if an exception is thrown if an item is already in the location that 
	//is trying to be added to
	@Test
	public void testAddItemFull(){
		machine.addItem(item1, "A");		
		thrown.expect(VendingMachineException.class);
		machine.addItem(item2, "A");
	}
	
	//Tests if an exception is thrown if the index passed in is invalid	
	@Test
	public void testAddItemInvalidCode(){				
		thrown.expect(VendingMachineException.class);
		machine.addItem(item1, "Z");
	}
	
	//Tests if you can add a null item
	@Test
	public void testAddItemNull(){			
		thrown.expect(VendingMachineException.class);
		machine.addItem(null, "A");	
	}
	
	//Tests if an exception is thrown if you try to remove an item that is not there
	@Test
	public void testRemoveItemEmpty(){
		thrown.expect(VendingMachineException.class);
		machine.removeItem("A");
	}
	
	//Tests if the remove function returns the correct item that is removed
	@Test
	public void testRemoveItemReturnedValue(){
		machine.addItem(item1, "A");
		assertEquals(machine.removeItem("A"), item1);
	}
	
	//Tests if the remove function actually removes the item
	@Test
	public void testRemoveItem(){
		machine.addItem(item1, "A");
		machine.removeItem("A");
		assertEquals(machine.getItem("A"), null);
	}
	
	//Tests if you can add money that is negative
	@Test 
	public void testInsertMoneyNegative(){
		thrown.expect(VendingMachineException.class);
		machine.insertMoney(-1);
	}
	
	//Tests if you can add money that is positive
	@Test 
	public void testInsertMoneyPositive(){
		machine.insertMoney(20);
		assertEquals(machine.getBalance(), 20, .05);
	}
	
	//Tests if you can add money that is zero
	@Test 
	public void testInsertMoneyZero(){
		machine.insertMoney(0);
		assertEquals(machine.getBalance(), 0, .05);
	}

	//Tests if the correct balance is returned when there is a balance
	@Test 
	public void testGetBalance(){
		machine.insertMoney(.75);
		assertEquals(machine.getBalance(), .75, .05);
	}
	
	@Test
	public void testGetBalanceZero(){
		assertEquals(machine.getBalance(), 0, .05);
	}
	
	//Tests if the correct balance is returned when there is no balance
	@Test 
	public void testGetBalanceEmpty(){
		assert(machine.getBalance() == 0);
	}
	
	@Test
	public void testGetBalanceNegative(){
		machine.balance = -5;
		thrown.expect(VendingMachineException.class);
		machine.getBalance();
		
	}
	
	//Tests if the method returns true when an item is successfully added
	@Test
	public void testMakePurchase(){
		machine.balance = 50;
		machine.addItem(item1, "A");		
		assertEquals(machine.makePurchase("A"), true);
	}
	
	//Tests if the method returns false when an item is unsuccessfully added
	@Test
	public void testMakePurchaseNoItem(){		
		assertEquals(machine.makePurchase("A"), false);
	}
	
	//Tests if the method will throw an exception if a code doesn't exist
	@Test
	public void testMakePurchaseInvalidCode(){
		thrown.expect(VendingMachineException.class);
		machine.makePurchase("Z");
	}
	
	@Test
	public void testMakePurchaseBalanceLessThanZero(){
		machine.addItem(item1, "A");
		machine.balance = -5;
		thrown.expect(VendingMachineException.class);
		machine.makePurchase("A");
	}
	
	//Tests if the return change returns the correct amount when there is a balance
	@Test 
	public void testReturnChange(){
		machine.insertMoney(1);
		assertEquals(machine.returnChange(), 1, .05);
	}
	
	//Tests if the return change returns the correct amount when there is no balance
	@Test 
	public void testReturnChangeEmpty(){		
		assertEquals(machine.returnChange(), 0, .05);
	}
	
	@Test
	public void testReturnChangeNegative(){
		machine.balance = -5;
		thrown.expect(VendingMachineException.class);
		machine.returnChange();
	}
	
	//Sets the expected exception thrown to none
	@After
	public void tearDown(){
		thrown = ExpectedException.none();
	}
	
}
